package com.example.server.handler;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.Future;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.net.InetSocketAddress;

/**
 * @author Gjing
 * <p>
 * 服务启动监听器
 **/
@Slf4j
@Component
public class NettyServer {
    private ServerBootstrap bootstrap;
    private EventLoopGroup bossGroup;
    private EventLoopGroup workGroup;
    private Channel serverChannel;

    @Value("${app.tcp.host}")
    private String host;
    @Value("${app.tcp.port}")
    private Integer port;
    @Autowired
    private ServerChannelInitializer serverChannelInitializer;

    @PostConstruct
    public void start() {
        InetSocketAddress socketAddress = new InetSocketAddress(host, port);
        //new 一个主线程组
        bossGroup = new NioEventLoopGroup(1);
        //new 一个工作线程组
        workGroup = new NioEventLoopGroup(200);
        bootstrap = new ServerBootstrap()
                .group(bossGroup, workGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(serverChannelInitializer)
                .localAddress(socketAddress)
                //设置队列大小
                .option(ChannelOption.SO_BACKLOG, 1024)
                // 两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文
                .childOption(ChannelOption.SO_KEEPALIVE, true);
        //绑定端口,开始接收进来的连接
        try {
            ChannelFuture future = bootstrap.bind(socketAddress).sync();
            serverChannel = future.channel();
            log.info("服务器启动开始监听端口: {}", socketAddress.getPort());
        } catch (InterruptedException e) {
            log.error(e.getMessage(), e);
        }
    }

    @PreDestroy
    public void shutdown() {
        try {
            if (this.serverChannel != null && this.serverChannel.isActive()) {
                this.serverChannel.close().sync();
            }
            this.bossGroup.shutdownGracefully();
            this.workGroup.shutdownGracefully();
        } catch (InterruptedException e) {
            log.error(e.getMessage(), e);
        }
    }
}